<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MyPromise</title>
</head>
<body>
    <script src="./ajax.js"></script>
    <script>

        const PADDING = Symbol('PADDING')
        const FULFILLED = Symbol('FULFILLED')
        const REJECTED = Symbol('REJECTED')

        // Promise 1.0 紧支持同步
        /*
            var MyPromise = function(fn) {
                this.state = PADDING
                this.value = null

                var resolve = function(value) {
                    if (this.state === PADDING) {
                        this.state = FULFILLED
                        this.value = value
                    }
                }.bind(this)
                var reject = function(err) {
                    if (this.state === PADDING) {
                        this.state = REJECTED
                        this.value = err
                    }
                }.bind(this)
                fn(resolve, reject)
            }
            MyPromise.prototype.then = function(onFulFilled, onRejected) {
                if (this.state === FULFILLED) {
                    onFulFilled(this.value)
                }
                if (this.state === REJECTED) {
                    onRejected(this.value)
                }
                return this
            }
        */

        // 1.1 MyPromise 支持异步
        /*
        var MyPromise = function(fn) {
            this.state = PADDING
            this.value = null
            this.onResolveCallbackList = []
            this.onRejectCallbackList = []

            var resolve = function(value) {
                if (this.state === PADDING) {
                    this.state = FULFILLED
                    this.value = value
                    this.onResolveCallbackList.forEach(function(fn) {
                        fn()
                    })
                }
            }.bind(this)

            var reject = function(err) {
                if (this.state === PADDING) {
                    this.state = REJECTED
                    this.value = err
                    this.onRejectCallbackList.forEach(function(fn) {
                        fn()
                    })
                }
            }.bind(this)
            try {
                fn(resolve, reject)
            } catch (error) {
                reject(error)
            }
        }
        MyPromise.prototype.then = function(onFulFilled, onRejected) {
            // 缓存 padding 的动作
            if (this.state === PADDING) {
                this.onResolveCallbackList.push(function () {
                    onFulFilled(this.value)
                }.bind(this))
                this.onRejectCallbackList.push(function () {
                    onRejected(this.value)
                }.bind(this))
            }
            if (this.state === FULFILLED) {
                onFulFilled(this.value)
            }
            if (this.state === REJECTED) {
                onRejected(this.value)
            }
            return this
        }
        */

        // 1.2 MyPromise 支持异步 支持链式调用
        var MyPromise = function(fn) {
            this.state = PADDING
            this.value = null
            this.onResolveCallbackList = []
            this.onRejectCallbackList = []

            var resolve = function(value) {
                if (this.state === PADDING) {
                    this.state = FULFILLED
                    this.value = value
                    this.onResolveCallbackList.forEach(function(fn) {
                        fn()
                    })
                }
            }.bind(this)

            var reject = function(err) {
                if (this.state === PADDING) {
                    this.state = REJECTED
                    this.value = err
                    this.onRejectCallbackList.forEach(function(fn) {
                        fn()
                    })
                }
            }.bind(this)
            try {
                fn(resolve, reject)
            } catch (error) {
                reject(error)
            }
        }
        
        /*
            p1
                p2
                    p3
                        p4
                            p...
        */

        MyPromise.prototype.then = function(onFulFilled, onRejected) {
            // 缓存 padding 的动作
            var self = this
            onRejected = typeof onRejected === 'function'  ? onRejected : function(c) { return c }
            onFulFilled = typeof onRejected === 'function'  ? onFulFilled : function(c) { return c }

            var pn = new MyPromise(function(resolve, reject) {
                if (self.state === PADDING) {
                    self.onResolveCallbackList.push(function () {
                        var x = onFulFilled(self.value)
                        resolvePromise(pn, x, resolve, reject)
                    }.bind(self))
                    self.onRejectCallbackList.push(function () {
                        var x = onRejected(self.value)
                        resolvePromise(pn, x, resolve, reject)
                    }.bind(self))
                }
                if (self.state === FULFILLED) {
                    var x = onFulFilled(self.value)
                    resolvePromise(pn, x, resolve, reject)
                }
                if (self.state === REJECTED) {
                    var x = onRejected(self.value)
                    resolvePromise(pn, x, resolve, reject)
                }
            })
            return pn;
        }

        // 必须等待所有的全部执行完毕, 才 resolve
        MyPromise.all = function(promiseList) {
            var arr = []
            var i = 0;
            return new MyPromise(function (resolve, reject) {
                for (var j = 0; j < promiseList.length; j++) {
                    (function(j) {
                        promiseList[j].then(function(data) {
                            arr[j] = data;
                            i++;
                            if (i === promiseList.length) {
                                resolve(arr)
                            }
                        }, reject) 
                    })(j);
                }
            })
        }

        // - 循环引用
        // - 是否为 promise
        // - 是否为普通的值
        // - 是否为有效值
        var resolvePromise = function(pn, x, resolve, reject) {
            // 循环引用
            if (x === pn) {
                return reject(new TypeError('语法错误: 循环引用'))
            }
            if (x !== null && ['object', 'function'].indexOf(typeof x) !== -1) {
                // 假设 x 是 promise 对象
                try {
                    var then = x.then
                    if (typeof then === 'function') {
                        x.then(function(val) {
                            // 假如 val 是 promise 类型
                            resolvePromise(pn, val, resolve, reject)
                        }, function(err) {
                            reject(err);
                        })
                    } else {
                        resolve(x);
                    }
                } catch (error) {
                    // 代码异常, 直接 reject
                    reject(error);
                }
            } else {
                // x 为基本类型
                resolve(x)
            }
        }

        // let p = new MyPromise(function(resolve, reject) {
        //     setTimeout(function() {
        //         resolve(10)
        //     }, 500)
        // })

        // p.then(function(v) {
        //     console.log(v, '111')
        //     return new MyPromise(function(resolve, reject) {
        //         setTimeout(function() {
        //             resolve(20)
        //         }, 500)
        //     })
        // })
        // .then(function(v) {
        //     console.log(v, '222')
        //     return 30
        // })
        // .then(function(v) {
        //     console.log(v, '333')
        // })


// ---------------------------------------------

        /**/
        window.onload = function() {

            let generatorPromiseApi = function({ url, method, success }) {
                return function() {
                    return new MyPromise(function(resolve, reject) {
                        ajax({
                            url,
                            method,
                            success: function(data) {
                                success && success(data)
                                resolve(data)
                            },
                            error: function(err) {
                                reject(err)
                            }
                        })
                    })
                }
            }
            var login = generatorPromiseApi({
                url: '/login',
                method: 'POST',
                success: function() {
                    console.log('/login')
                }
            })
            var getVipInfo = generatorPromiseApi({
                url: '/getVipInfo',
                method: 'POST',
                success: function() {
                    console.log('/getVipInfo')
                }
            })
            var isRemind = generatorPromiseApi({
                url: '/isRemind',
                method: 'POST',
                success: function() {
                    console.log('/isRemind')
                }
            })

            // 无序调用
            let pList = [
                login(),
                getVipInfo(),
                isRemind()
            ]

            MyPromise.all(pList)
                .then(function(data) {
                    console.log(data);
                })

            // 同步调用
            // login()
            //     .then(function(data) {
            //         console.log(2, data)
            //         return getVipInfo()
            //     })
            //     .then(function(data) {
            //         console.log(3, data)
            //         return isRemind()
            //     })

        }
    
    </script>
</body>
</html>